package juc.popular;

import lombok.NonNull;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

/**
 * java中的线程池
 * <p>
 * 构造方法(核心线程数,最大线程数,线程空闲存活时间,时间单位,任务队列,队列满时的处理策略)
 * * {@link ThreadPoolExecutor#ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue)}
 * * {@link ThreadPoolExecutor#ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, ThreadFactory)}
 * * {@link ThreadPoolExecutor#ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, RejectedExecutionHandler)}
 * * {@link ThreadPoolExecutor#ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, ThreadFactory, RejectedExecutionHandler)}
 * <p>
 * 创建方法
 * * {@link Executors#newCachedThreadPool()}核心线程为0,最大线程数为Integer.MAX_VALUE,如果不控制线程数有可能会造成系统OOM
 * * {@link Executors#newFixedThreadPool(int)}核心线程数==最大线程数,线程池中没有可运行任务时,也不会释放工作线程
 * * {@link Executors#newSingleThreadExecutor()}单例的线程池
 * * {@link Executors#newSingleThreadScheduledExecutor()}单例的定时线程池,可保证任务顺序执行
 * * {@link Executors#newScheduledThreadPool(int)}指定核心线程数的定时线程池
 * <p>
 * 主要参数
 * * 1. corePoolSize: 核心线程数
 * * * 核心线程会一直存活,及时没有任务需要执行
 * * * 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
 * * * 设置allowCoreThreadTimeout=true（默认false）时,核心线程会超时关闭
 * *
 * * 2. queueCapacity: 任务队列容量（阻塞队列）
 * * * 当核心线程数达到最大时,新任务会放在队列中排队等待执行
 * *
 * * 3. maxPoolSize: 最大线程数
 * * * 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
 * * * 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
 * *
 * * 4. keepAliveTime: 线程空闲时间
 * * * 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
 * * * 如果allowCoreThreadTimeout=true,则会直到线程数量=0
 * * * allowCoreThreadTimeout: 允许核心线程超时
 * *
 * * 5. rejectedExecutionHandler: 任务拒绝处理器
 * * * AbortPolicy 丢弃任务,抛运行时异常(默认配置)
 * * * CallerRunsPolicy 在当前主线程中执行被丢弃的任务(不推荐,会造成主线程阻塞)
 * * * DiscardPolicy 忽视,什么都不会发生
 * * * DiscardOldestPolicy 从队列中踢出最后一个执行的任务
 * * * 实现RejectedExecutionHandler接口,可自定义处理器
 * <p>
 * * 两种情况会拒绝处理任务:
 * * * 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
 * * * 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
 * * * 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
 * <p>
 * 线程池的执行
 * * 当线程数小于核心线程数时,创建线程
 * * 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列
 * * 当线程数大于等于核心线程数,且任务队列已满
 * * * 若线程数小于最大线程数,创建线程
 * * * 若线程数等于最大线程数,抛出异常,拒绝任务
 * <p>
 *
 * @author 王霄
 * @date 2021/9/14 13:11
 **/
public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService service = create();
        IntStream.range(1, 100)
                .forEach(x -> service.submit(() -> System.out.println(x)));
    }

    public static ExecutorService create() {
        return new ThreadPoolExecutor(
                10,
                15,
                60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100),
                new ThreadFactory() {
                    private final AtomicInteger number = new AtomicInteger(0);

                    @Override
                    public Thread newThread(@NonNull Runnable r) {
                        String prefix = "test-pool";
                        return new Thread(r, prefix + number.incrementAndGet());
                    }
                },
                (r, e) -> {
                    throw new RejectedExecutionException("任务队列已满,抛弃任务" + e.toString());
                });
    }
}
